module M_receive
(
    input                I_clk           , // 系统50MHz时钟
    input                I_rst_n         , // 系统全局复位
    input                I_iic_recv_en   , // IIC发送使能位
    
    input        [6:0]   I_dev_addr      , // IIC设备的物理地址
    input        [7:0]   I_word_addr     , // IIC设备的字地址，即我们想操作的IIC的内部地址
    output  reg  [7:0]   O_read_data     , // 从IIC设备的字地址读出来的数据   

	 output               O_done_flag     , // 读或写IIC设备结束标志位
	 
    output               O_scl           , // IIC总线的串行时钟线
	 output               O_sda           , // IIC总线的主向从sda数据线
	 input                I_sda             // IIC总线的从向主sda数据线
);          
 

reg     [9:0]   R_scl_cnt       ; // 用来产生IIC总线SCL时钟线的计数器   
reg     [3:0]   R_state         ; 
reg             R_sda_mode      ; // 设置SDA模式，1位输出，0为输入
reg             R_sda_reg       ; // SDA寄存器
reg     [7:0]   R_load_data     ; // 发送/接收过程中加载的数据，比如设备物理地址，字地址和数据等
reg     [3:0]   R_bit_cnt       ; // 发送字节状态中bit个数计数
reg             R_ack_flag      ; // 应答标志
reg     [3:0]   R_jump_state    ; // 跳转状态，传输一个字节成功并应答以后通过这个变量跳转到导入下一个数据的状态
reg     [7:0]   R_read_data_reg ; // 接收数据寄存器
reg             R_flag           ; 


assign O_sda  =  (R_sda_mode == 1'b1) ? R_sda_reg : 1'bz ;


assign O_scl           =  I_clk      ; // 产生串行时钟信号O_scl
assign O_done_flag     =  R_flag     ;

always @(O_scl or I_rst_n) // posedge O_scl or negedge I_rst_n
begin
    if(!I_rst_n)
        begin
            R_state         <=  4'd0 ;
            R_sda_mode      <=  1'b0 ;
            R_sda_reg       <=  1'b1 ;
            R_bit_cnt       <=  4'd0 ;
            R_flag          <=  1'b0 ;
				R_load_data     <=  8'd0 ;
            R_jump_state    <=  4'd0 ;
            R_read_data_reg <=  8'd0 ;
            R_ack_flag      <=  1'b0 ;
            O_read_data     <=  8'd0 ;
        end
    else if(I_iic_recv_en) // 往IIC设备发送数据
        begin
            case(R_state)
                4'd0    :   // 空闲状态，用来初始化相关所有信号
                    begin
                        R_sda_mode      <=  1'b1 ; // 设置SDA为输出
                        R_sda_reg       <=  1'b1 ; // 设置SDA为高电平
                        R_state         <=  4'd3 ; // 直接进入发送起始信号，再加载数据
                        R_bit_cnt       <=  4'd0 ;
                        R_jump_state    <=  4'd0 ;
                        R_read_data_reg <=  8'd0 ;
                    end
                4'd1    :   // 加载IIC设备物理地址 
                    begin
                        R_load_data <=  {I_dev_addr, 1'b0}  ; 
                        R_state     <=  4'd4                ; // 加载完设备物理地址以后进入发送状态
                        R_jump_state <=  4'd2     ; 
                    end
                4'd2   :   // 加载IIC设备字地址                     
                    begin                                   
                        R_load_data <=  I_word_addr         ; 
                        R_state     <=  4'd4                ;
                        R_jump_state <=  4'd6      ; // 设置这里是为了这一轮发送并应答后跳到第二次启始位
                    end 
                4'd3    :   // 发送第一个起始信号
                    begin
                        R_sda_mode  <=  1'b1                ; // 设置SDA的模式为输出
                        if(O_scl == 1'b1)
                            begin
                                R_sda_reg   <=  1'b0        ; // 在SCL高电平的正中间把SDA引脚拉低产生一个下降沿
                                R_state     <=  4'd1        ; ///////////变成先发送起始信号再加载  
                            end
                        else
                            R_state <=  4'd3                ;    
                    end                      
                4'd4    :   // 发送一个字节
                    begin
                        R_sda_mode  <=  1'b1                ; // 设置SDA的模式为输出 ///////////
                        if(O_scl == 1'b0)                     // 在SCL低电平的最中间改变数据
                            begin
                                if(R_bit_cnt == 4'd8)
                                    begin
                                        R_bit_cnt  <=  4'd0 ;  
                                        R_state    <=  4'd5 ;
                                    end 
                                else
                                    begin
												    //R_sda_mode  <=  1'b1                ;
                                        R_sda_reg  <=  R_load_data[7-R_bit_cnt] ;
                                        R_bit_cnt  <=  R_bit_cnt + 1'b1         ;
                                    end     
                            end
                        else
								    begin
                                R_state <=  4'd4    ;	
									 end 
                    end
                4'd5    :   // 接收应答状态应答位，并校验
                    begin
								R_sda_mode  <=  1'b1 ; // 设置SDA的模式为输入 // 1'b0
                        R_sda_reg   <=  1'b0 ; // 这里提前将sda线拉低是为了防止直接进入未应答的停止态
                        if(O_scl == 1'b1)  
                            begin
                                R_ack_flag  <=  I_sda   ;
                                if(R_ack_flag   == 1'b0)    // 校验通过
                                    begin
                                        R_state <=  R_jump_state ;
                                        //R_sda_mode  <=  1'b1 ; // 设置SDA的模式为输出
                                        R_sda_reg   <=  1'b1 ; // 设置SDA的引脚电平拉高，方便后面产生第二次起始位//////////
                                    end
                                else if(R_ack_flag   == 1'b1)/////////////////
                                    R_state <=  4'd0 ;     //////////第二次校验失败？？？
                                else
                                    R_state <=  4'd5    ; //如果此时ack为1'hz则再等一次										  
                            end
                        else
                            R_state <=  4'd5    ;    
                    end                      
                4'd6    :   // 第二次起始位(IIC读操作要求有2次起始位)   ，由状态5直接跳到7并执行？？？
                    begin
                        R_sda_mode  <=  1'b1 ; // 设置SDA的模式为输出 
                        if(O_scl == 1'b1)
                            begin
                                R_sda_reg   <=  1'b0  ;
										  R_load_data     <=  {I_dev_addr, 1'b1}; // 前7bit是设备物理地址，最后一位1表示读操作
                                R_state         <=  4'd4 ;
                                R_jump_state    <=  4'd7 ;
                            end
                        else if(O_scl == 1'b0)
								    begin
                                R_state <=  4'd6 ;
										  R_sda_reg   <=  1'b0  ;
						          end			 
                    end 
                4'd7    :   // 读一个字节数据
                    begin
                        R_sda_mode  <=  1'b0 ; // 设置SDA的模式为输入
                        if(O_scl == 1'b1)
                            begin
                                if(R_bit_cnt == 4'd8)
                                    begin
                                        R_bit_cnt    <=  4'd0    ;
                                        R_state      <=  4'd8   ;  
                                        O_read_data  <=  {R_read_data_reg[6:0],I_sda}  ;                                           
                                    end
                                else
                                    begin
                                        R_read_data_reg  <=  {R_read_data_reg[6:0],I_sda}  ;
                                        R_bit_cnt        <=  R_bit_cnt   +   1'b1            ;  
                                    end    
                            end 
                        else
                            R_state <=  4'd7 ;    
                    end 
                4'd8    :  // 读完一个字节数据以后进入10，主机发送一个非应答信号1
                    begin
                        R_sda_mode  <=  1'b1 ; // 设置SDA的模式为输出
                        if(O_scl == 1'b0)
                            begin
                                R_state     <=  4'd9   ; 
                                R_sda_reg   <=  1'b1    ;
                            end 
                        else
                            R_state <=  4'd8 ;    
                    end 
                4'd9   :  
                    begin
                        R_sda_mode  <=  1'b1 ; // 设置SDA的模式为输入
                        if(O_scl == 1'b0)
                            begin
                                R_state     <=  4'd10   ; 
                                R_sda_reg    <=    1'b0     ;  // 发送完未应答后在紧接着的scl低电平提前拉低sda线
                            end
                        else
                            R_state <=  4'd9   ;    
                    end 
                4'd10   : //停止位Stop
                    begin
                        R_sda_mode  <=  1'b1 ; // 设置SDA的模式为输出
                        if(O_scl == 1'b1)
                            begin
                                R_sda_reg   <=  1'b1    ;
                                R_state     <=  4'd11   ; 
                            end
                        else
                            R_state <=  4'd10   ;    
                    end 
                4'd11   :
                    begin
                        R_sda_mode      <=  1'b1 ; // 设置SDA为输出
                        R_sda_reg       <=  1'b1 ; // 拉高SDA保持空闲状态情况
                        R_flag          <=  1'b1 ;
                        R_state         <=  4'd0 ;
                        R_read_data_reg <=  8'd0 ;
                    end 
                default: R_state         <=  4'd0 ;
            endcase
        end 
    else
        begin
            R_state         <=  4'd0 ;
            R_sda_mode      <=  1'b1 ;
            R_sda_reg       <=  1'b1 ;
            R_bit_cnt       <=  4'd0 ;
            R_flag          <=  1'b1 ;  
            R_jump_state    <=  4'd0 ;
            R_read_data_reg <=  8'd0 ;
            R_ack_flag         <=  1'b0 ;
        end
end
endmodule